Skip to content

Fix Unicode titlecase, folding and encoded case mappings - #38

Draft
284km wants to merge 2 commits into
perf/reuse-scratch-scopesfrom
fix/unicode-case-compatibility
Draft

284km wants to merge 2 commits into
perf/reuse-scratch-scopesfrom
fix/unicode-case-compatibility

Conversation

@284km

@284km 284km commented Nov 7, 2026 •

Copy link
Copy Markdown
Owner

Unicode case conversion used uppercase-plus-lowercase as a substitute for titlecase, applied final-sigma context rules that Ruby does not use, and omitted identity exceptions from case folding. Correct capitalize, swapcase and downcase(:fold), including Cherokee, titlecase digraphs and Turkic cases. Preserve the receiver encoding when ASCII input maps to non-ASCII bytes. Accept the valid single byte 0x80 in GBK/CP936 and CP949 during input validation.

Generate titlecase and swapcase exceptions from the pinned reference Ruby, retain identity folding exceptions, and generate a 490-byte page mask to avoid hexadecimal-key construction and table lookups where no mapping exists. The mask includes every table entry, including identity exceptions. Regressions cover UTF-8/16/32, single-byte encodings, bang methods, multibyte validation and unmapped Unicode pages.

Depends on #39; merge that PR first. The temporary-scope fix reduced the complete Unicode run from 590.6 to 539.5 seconds locally, preserving all 422 tests and 2,901,771 assertions with zero failures/errors/skips. The final head, including the page mask, also passes in 549.0 seconds with 2,005,648 KiB peak RSS. This does not establish an additional full-suite speedup from the mask; CI still needs to confirm the remaining headroom. The original head exceeded the CI wall limit, so this PR remains draft until the final-head CI confirms completion under the unchanged 600-second/2-GiB bounds.

Validation:

  • All five generated mapping tables remain byte-identical after adding the mask; all 3,525 entries occupy marked pages.
  • The expanded regression matches Ruby 4.0.9 with hash indexing ON/OFF, including GC stress, on macOS.
  • The inherited scratch-frame regression passes with indexing ON/OFF (3 fresh frames at both 100 and 10,000 iterations).
  • The final head passes all 523 corpus programs with hash indexing ON/OFF. The expanded Unicode regression also passes on Linux at O2 with indexing ON/OFF.
  • Final-head comprehensive Unicode: 422 tests, 2,901,771 assertions, zero failures/errors/skips, within the unchanged local bounds.
  • A focused workload (source = "漢字𠀀🙂A"; 100_000.times { raise unless source.swapcase == "漢字𠀀🙂a" }) reduced case-walker region allocations from 96,914,128 to 39,200,000 bytes with the page mask. One local run took 0.47 → 0.42 seconds; the allocation count is the stronger evidence, not a full-suite timing claim.

@284km 284km left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed ad15079 against e45622a. No actionable findings introduced by this change.

The dedicated titlecase/swapcase exceptions and identity-preserving fold entries address the mapping differences directly. I also checked the case-method dispatch, bang-method behavior, legacy-encoding path, and every updated call to the double-byte validator.

Validation:

  • Fresh O2 build with the CI-pinned Mere 991f299b compiler and mgz revision.
  • Regenerated all five tables with Ruby 4.0.9: every table matches exactly, including 86 identity fold exceptions and 77 identity titlecase exceptions.
  • The new corpus regression matched Ruby with indexing ON/OFF, normally and with MERE_RUBY_GC_STRESS=100.
  • Exhaustive single-byte probes across the 18 affected encodings and all 256 byte values matched all 78,336 reference rows. These cover ordinary/:ascii/:turkic/:lithuanian options, folding, result bytes/encoding/validity, and bang results.
  • Mixed-string probes over the 401 distinct fold/title/swap exception codepoints matched all 20,050 mapping rows across UTF-8, UTF-16LE/BE, and UTF-32LE/BE, including ASCII/Turkic modes and folding.
  • The repository runner completed the focused upstream case-mapping/options files: 18/21 tests matched. The three residual failures also reproduce on the earlier baseline; the mapping file improved from 13/18 to 17/18. These residual compatibility failures are not attributed to this patch.
  • CI for this head reports 523/523 corpus programs matching Ruby and a successful Linux build. The comprehensive Unicode file is assigned to shard 5, which remains pending along with other jobs.
  • git diff --check passed.

I did not repeat the full comprehensive Unicode file or Linux execution locally. The reported full-suite result and its narrow margin under the 600-second limit still need the pending CI measurement to confirm the available headroom.

Comment thread tools/gen_case_tables.rb Resolved
Comment thread m_unicode.mere Resolved
@284km

284km commented Nov 7, 2026

Copy link
Copy Markdown
Owner Author

Thanks for the detailed table regeneration and encoding checks. No code changes are needed for the two positive findings. The remaining concern is confirmed by the CI artifact: the comprehensive Unicode file reached the existing 600-second wall limit after 354 cases, so I am keeping this PR in draft. I am measuring it with the temporary-scope allocation fix in #39 before deciding the integration order.

@284km
284km changed the base branch from main to perf/reuse-scratch-scopes November 7, 2026 12:47

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant